package cn.conac.rc.gateway.base;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

import com.alibaba.fastjson.JSONObject;
import com.netflix.hystrix.HystrixCommand;

import cn.conac.rc.framework.utils.RestClientException;

/**
 * ClassName: BaseService
 * @author wangmeng
 * @version 1.0
 */
@SuppressWarnings("unused")
public class BaseService {

    public BaseService() {
    }

    /**
     * restTemplate
     */
    protected RestTemplate restTemplate;

    /**
     * interceptor
     */
    @Autowired
    protected LoadBalancerInterceptor interceptor;

    /**
     * setInterceptor.
     * @param interceptor LoadBalancerInterceptor
     */
    @Autowired
    public void setInterceptor(LoadBalancerInterceptor interceptor) {
        this.interceptor = interceptor;
        restTemplate = new RestTemplate();
        ((SimpleClientHttpRequestFactory) restTemplate.getRequestFactory()).setReadTimeout(1000 * 50);
        List<ClientHttpRequestInterceptor> list = new ArrayList<ClientHttpRequestInterceptor>();
        list.add(interceptor);
        restTemplate.setInterceptors(list);
    }

    /**
     * GET(getForObject).
     * @param <T> 泛型对象
     * @param url String
     * @param timeOut int
     * @param responseType Class<T>
     * @param urlVariables Object
     * @return T
     * @throws RestClientException RestClient异常
     */
    public <T> T getForObject(String url, int timeOut, Class<T> responseType, Object... urlVariables)
            throws RestClientException
    {
        T obj = restTemplate.getForObject(url, responseType, urlVariables);
        int code = ((JSONObject) obj).getIntValue("code");
        String msg = ((JSONObject) obj).getString("msg");
        if (code > 1999) {
            throw new RestClientException(code, msg);
        } else {
            return obj;
        }
    }

    /**
     * GET(getForObject).
     * @param <T> 泛型对象
     * @param url String
     * @param responseType Class<T>
     * @param urlVariables Object
     * @return T
     * @throws RestClientException RestClient异常
     */
    public <T> T getForObject(String url, Class<T> responseType, Object... urlVariables) throws RestClientException {
        T obj = restTemplate.getForObject(url, responseType, urlVariables);
        int code = ((JSONObject) obj).getIntValue("code");
        String msg = ((JSONObject) obj).getString("msg");
        if (code > 1999) {
            throw new RestClientException(code, msg);
        } else {
            return obj;
        }
    }

    /**
     * POST(postForObject).
     * @param <T> 泛型对象
     * @param url String
     * @param timeOut int
     * @param request Object
     * @param responseType Class<T>
     * @param uriVariables Object
     * @return T
     * @throws RestClientException RestClient异常
     */
    public <T> T postForObject(String url, int timeOut, Object request, Class<T> responseType, Object... uriVariables)
            throws RestClientException {
        T obj = restTemplate.postForObject(url, request, responseType, uriVariables);
        int code = ((JSONObject) obj).getIntValue("code");
        String msg = ((JSONObject) obj).getString("msg");
        if (code > 1999) {
            throw new RestClientException(code, msg);
        } else {
            return obj;
        }
    }

    /**
     * POST(postForObject).
     * @param <T> 泛型对象
     * @param url String
     * @param request Object
     * @param responseType Class<T>
     * @param uriVariables Object
     * @return T
     * @throws RestClientException RestClient异常
     */
    public <T> T postForObject(String url, Object request, Class<T> responseType, Object... uriVariables)
            throws RestClientException {
        T obj = restTemplate.postForObject(url, request, responseType, uriVariables);
        int code = ((JSONObject) obj).getIntValue("code");
        String msg = ((JSONObject) obj).getString("msg");
        if (code > 1999) {
            throw new RestClientException(code, msg);
        } else {
            return obj;
        }
    }

    // PUT
    private static final long TIMEOUT = 2 * 60 * 1000;

    private static Map<String, Map<String, Object>> doBackendAsyncServiceCall(List<Callable<AsyncResponse>> callables) {
        ExecutorService executorService = Executors.newFixedThreadPool(4);
        try {
            List<Future<AsyncResponse>> futures = executorService.invokeAll(callables);
            executorService.shutdown();
            executorService.awaitTermination(TIMEOUT, TimeUnit.MILLISECONDS);
            Map<String, Map<String, Object>> result = new HashMap<>();
            for (Future<AsyncResponse> future : futures) {
                AsyncResponse response = future.get();
                result.put(response.serviceKey, response.response);
            }
            return result;
        } catch (InterruptedException | ExecutionException e) {
            throw new RuntimeException(e);
        }
    }

    private static class AsyncResponse {
        private final String serviceKey;
        private final Map<String, Object> response;

        AsyncResponse(String serviceKey, Map<String, Object> response) {
            this.serviceKey = serviceKey;
            this.response = response;
        }
    }

    private static class BackendServiceCallable implements Callable<AsyncResponse> {
        private final String serviceKey;
        private final HystrixCommand<Map<String, Object>> hystrixCommand;

        public BackendServiceCallable(String serviceKey, HystrixCommand<Map<String, Object>> hystrixCommand) {
            this.serviceKey = serviceKey;
            this.hystrixCommand = hystrixCommand;
        }

        @Override
        public AsyncResponse call() throws Exception {
            return new AsyncResponse(serviceKey, hystrixCommand.execute());
        }
    }
    
//    //例子
//    @Cacheable
//    private HystrixCommand<Map<String, Object>> getProductDetails(String productId) {
//        return new HystrixCommand<Map<String, Object>>(
//                HystrixCommand.Setter
//                        .withGroupKey(HystrixCommandGroupKey.Factory.asKey(GROUP))
//                        .andCommandKey(HystrixCommandKey.Factory.asKey("getProductDetails"))
//                        .andCommandPropertiesDefaults(
//                                HystrixCommandProperties.Setter()
//.withExecutionIsolationThreadTimeoutInMilliseconds(TIMEOUT)
//                        )
//        ) {
//            @Override
//            protected Map<String, Object> run() throws Exception {
//                return productDetailService.getDetails(productId);
//            }
//            @Override
//            protected Map getFallback() {
//                return new HashMap<>();
//            }
//        };
//    }

}
